#ifndef FILE_SE_Trace_HPP
#define FILE_SE_Trace_HPP

#include <string>
#include <fstream>
#include "NonCopyable.hpp"
#include "System.hpp"

namespace se{
/********************************************//**
 * Allow access to the log.
 *
 * You can set this class to output to any ostream.
 * It may be the console, a file, or maybe a network stream.
 * You can even set a specific stream per trace type, ie
 * the console for temporary debuging data, and the log file
 * for the rest.
 *
 * This class can will attempt to exit the program while 
 * running if it cannot output on some chanels.
 * This class set an handler on the system signals, to be
 * able to finish its writing job watever the circonstances.
 *
 * For now, it is designed as a singleton, enforced trough 
 * s_instanceCreated.
 *
 * How to use : call the configuration methods, and when you
 * are done, call Ready() to start the logging thread. You
 * may then call write to output to log, or call the macros
 * defined above.
 * \see SE_TRACE
 * \see SE_LOG
 * \see SE_LOGC
 ***********************************************/
class System::Trace{
	DISALLOW_COPY_AND_ASSIGN(Trace);
	// Nested types :
		public:
		/********************************************//**
		 * Defines the type of message for the Trace.
		 * 
		 * It changes the output channel, and the way they
		 * are displayed.
		 * Please note that if you add another value here,
		 * you need to edit the switch statement in Trace(), 
		 * and all the Write(MessageType...).
		 * 
		 * Note : I know hardcoding is a bad idea, but the Trace
		 * need to be very stable. And a Rube Goldberg machine is
		 * rarely stable. (The question is : will my method be
		 * stable ?)
		 ***********************************************/
			enum MessageType_e{
				DEBUG               = 0,
				EXCEPTION_HANDLED   = 1,
				EXCEPTION_UNHANDLED = 2,// ie : Boom !
				LOADED              = 3,
				LOAD_FAILED         = 4,
				SCRIPT_OUTPUT       = 5,
				SCRIPT_ERROR        = 6,
				INFO                = 7,
				//---------------------- ! Has to stay the last !
				NONE                = 8 
			};
			
			/********************************************//**
			 * Flags representing an output type.
			 *
			 * Be happy, you even have some room for your own outputs...
			 * But don't forget to add their handler at the right place.
			 ***********************************************/
			enum OutputTypeFlags_e{
				OS_CONSOLE    = 0x01,// 00000001
			//	GAME_CONSOLE  = 0x02,// 00000010
				LOG_FILE      = 0x04,// 00000100
			//	NETWORK       = 0xF0,// 10000000
				// I have no idea of how the network might work, but it might be
				// usefull some day.
			};
		
		protected:
			class TraceQueue;
			
			/// \brief Interface between the trace methods, and the writing thread.
			// Keeping a struct size of 256 is intended.
			struct TraceOutput_t{
				char text[249];
				Flags8_t channels;
				Color_t color;
				bool critical;
			};
	
	// Methods :
		public:
			Trace();
			
			/* Setup the channels. Do not do this while "on the air".
			 * Ideally, it should be the absolute first thing to do. Anyway
			 * while you haven't called Ready, the trace will be useless.
			 * And will be killing your thread with no warning !
			 */
			void   SetChanelOutputsForType( MessageType_e messageType, OutputTypeFlags_e outputChannelsToSet );
			void    AddChanelOutputsByType( MessageType_e messageType, OutputTypeFlags_e outputChannelsToAdd );
			void RemoveChanelOutputsByType( MessageType_e messageType, OutputTypeFlags_e outputChannelsToRemove );
			void ConfigureConsole(bool critical /* Many parameters. Or not. There is only one console.*/);
			//void ConfigureGameConsole(/* many parameters. but it don't even exist yet, so... */);
			void ConfigureLogFile( const char* filename, bool append = false, bool critical = true );
			bool Ready();
			// Write. using char* most of the time because they are more stable.
			// The maximum message size is 220, same for the file size.
			// Any longer will cause an error to be writen to the log.
			void Write(const std::string& message);
			void Write(MessageType_e messageType , const char* message, bool critical = false, Color_t col = Color_t());
			
			// Big trouble handlers. If I can say "handler"...
			void MemoryAllocationFailure();
			void LogOutputFailure();// What the heck am I supposed to do ?
			
			~Trace();
		protected:
			void WriteRaw(const char* c);
			static void RunThread(void* _trace);
		private:
			static void capMessage(TraceOutput_t* message, uint8_t& size);
	
	
	// Attributes :
		private :
			// If ready have been called.
			bool m_started;
			// The flags.
			uint8_t m_flag_messageType_Array[NONE];
			// Console.
			bool m_isCriticalConsole;
			// Log file.
			bool m_isCriticalLogFile;
			std::string m_nameLogFile;//for an eventual reopening
			std::ofstream m_logFile;
			bool m_flushOnFirstUseLogFile;
			
			// The thread part (the idea is to avoid hanging on IOWAIT).
			TraceQueue* m_traceQueue;
			ExceptionSafeThread m_fileWritingThread;
			ThreadSignal_t m_threadRequestFinish;
	
	// Static attributes :
		private :
			static const char* s_LogDefaultFilename;
			static ThreadSignal_t s_instanceCreated;
};

}// namespace se

#endif // FILE_SE_Trace_HPP
